unit utils;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, clases, Forms, Process, Dialogs, FileUtil, Registry,
  globaldatos, LCLType, lclintf;

function MkvValido(TDatos : TStringList) : Boolean;
function ObtenerPistasMkv(TDatos : TStringList) : TStringList;
function BuscarCoincidencias(Lista: TStringList; Const Busqueda: String): TStringList;
procedure GenerarAVS(Archivo : String; Subtitulo : String = 'nada');
function EjecucionConsola(Comando, Parametros: String; var SalidaLista : TStringList): Integer;
function EjecucionConsolaInfo(Comando, Parametros: String; var SalidaLista : TStringList): Integer;
procedure LimpiarAnalisis(var TDatos : TStringList);
function ObtenerVelocidadCodificacion(Velocidad: String): String;
procedure GenerarLog(Log : String = '');
procedure VerificarRequeridos;
procedure VerificarSistema;
procedure AviSynth;
function ObtenerExtension(s : String): String;

implementation

function ObtenerExtension(s : String): String;
Begin
  Case s Of
    'V_MPEG1' : Result := 'mpg';
    'A_AAC' : Result := 'aac';
    'A_AC3' : Result := 'ac3';
    'A_DTS' : Result := 'dts';
    'A_FLAC' : Result := 'flac';
    'A_APE' : Result := 'ape';
    'A_QUICKTIME' : Result := 'qdm';
    'A_TTA1' : Result := 'tta';
    'A_WAVPACK4' : Result := 'wv';
    'A_VORBIS' : Result := 'ogg';
    'A_REAL' : Result := 'ra';
    'V_MPEG2' : Result := 'mpg';
    'V_REAL' : Result := 'rmvb';
    'V_MS/VFW/FOURCC' : Result := 'avi';
    'V_MPEG4/ISO/AVC' : Result := 'h264';
    'S_VOBSUB' : Result := 'idx';
    'A_MPEG/L3' : Result := 'mp3';
    'A_MPEG/L2' : Result := 'mp2';
    'A_MPEG/L1' : Result := 'mpa';
    'A_PCM/INT/LIT' : Result := 'wav';
    'S_HDMV/PGS' : Result := 'sup';
    'S_TEXT/UTF8' : Result := 'srt';
    'S_TEXT/SSA' : Result := 'ssa';
    'S_TEXT/ASS' : Result := 'ass';
    'S_TEXT/USF' : Result := 'usf';
  end;
  //
{ EXTENSIONES (obtenidas de MKVExtractGUI 1.6.4.1
    'V_MPEG1=mpg'
    'A_AAC=aac'
    'A_AC3=ac3'
    'A_DTS=dts'
    'A_FLAC=flac'
    'A_APE=ape'
    'A_QUICKTIME=qdm'
    'A_TTA1=tta'
    'A_WAVPACK4=wv'
    'A_VORBIS=ogg'
    'A_REAL=ra'
    'V_MPEG2=mpg'
    'V_REAL=rmvb'
    'V_MS/VFW/FOURCC=avi'
    'V_MPEG4/ISO/AVC=h264'
    'S_VOBSUB=idx'
    'A_MPEG/L3=mp3'
    'A_MPEG/L2=mp2'
    'A_MPEG/L1=mpa'
    'A_PCM/INT/LIT=wav'
    'S_HDMV/PGS=sup'
    'S_TEXT/UTF8=srt'
    'S_TEXT/SSA=ssa'
    'S_TEXT/ASS=ass'
    'S_TEXT/USF=usf';

 }
end;

procedure VerificarSistema;
Begin
  If Is64bitOS Then Set64Bits(True) Else Set64Bits(False);
end;

procedure AviSynth;
var
  ListaTemporal : TStringList;
  Directorio : TFileName;
Begin
  ListaTemporal := TStringList.Create;

  Directorio := ExtractFilePath(Application.ExeName) + 'tools\avisynth\Avisynth_258.exe';
  If Application.MessageBox('No se ha encontrado AviSynth instalado en su sistema, este es necesario para el correcto funcionamiento de los ' +
      'filtros, ¿Desea instalarlo ahora?', 'Pregunta', MB_YESNO+MB_ICONQUESTION) = ID_YES Then Begin
      If FileExistsUTF8(Directorio) Then begin
         If (EjecucionConsolaInfo(Directorio, '', ListaTemporal)) <> 0 Then
            Application.MessageBox('Al parecer AviSynth no ha sido instalado de forma correcta. No olvidar que AnimeManiac necesita de este programa para codificar correctamente', 'Información', MB_ICONWARNING)
         else
            Application.MessageBox('AviSynth ha sido instalado.', 'Información', MB_ICONINFORMATION);
      end
      else begin
           OpenURL('http://www.avisynth.org');
      end;
  end;
end;

procedure VerificarRequeridos;
var
  Registro : TRegistry;
Begin
  Registro := TRegistry.Create;

  { verificamos si está instalado avisynth }
  Registro.RootKey := HKEY_LOCAL_MACHINE;
  if Not Registro.OpenKey('\SOFTWARE\AviSynth', false) then begin
     AviSynth;
  end;
    Registro.Free;

end;

procedure GenerarLog(Log : String = '');
var
  f : TextFile;
  DirectorioPrograma : String;
  Archivo : String;
Begin
  DirectorioPrograma := ExtractFilePath(Application.ExeName) + 'logs\';
  Archivo := DirectorioPrograma + 'programa.log';
  AssignFile(f, UTF8ToAnsi(DirectorioPrograma + 'programa.log'));
  {$I-}
    If FileExistsUTF8(Archivo) Then Append(f)
    Else Rewrite(f);
  {$I+}
  If Log <> '' Then
     If IOResult = 0 Then Begin
        Write(f, '['+TimeToStr(Time)+'] ' + Log);
        WriteLn(f);
     end;

  CloseFile(f);

end;

function EjecucionConsolaInfo(Comando, Parametros: String; var SalidaLista : TStringList): Integer;
const
   READ_BYTES = 2048;
var
   S: TStringList;
   M: TMemoryStream;
   P: TProcess;
   n: LongInt;
   BytesRead: LongInt;
Begin
     M := TMemoryStream.Create;
     BytesRead := 0;

     P := TProcess.Create(nil);
//     P.Executable := Comando;
//     P.Parameters.Add(UTF8ToAnsi(Parametros));
     If Parametros = '' Then P.CommandLine := UTF8ToSys(Comando)
     else P.CommandLine := UTF8ToSys(Comando + ' ' + Parametros);

     //ShowMessage(P.CommandLine);
     P.Options := [poUsePipes, poNoConsole];
     // WriteLn('-- ejecutando --');
     P.Execute;
     while P.Running do
     begin
       // nos aseguramos de tener espacio
       M.SetSize(BytesRead + READ_BYTES);

       // intentamos leerlo
       n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
       if n > 0
       then begin
         Inc(BytesRead, n);
         //Write('.')
       end
       else begin
         // sin datos, esperamos 100 milisegundos
         Sleep(100);
       end;
     end;
     // leemos la última parte
     repeat
       // nos aseguramos de tener espacio
       M.SetSize(BytesRead + READ_BYTES);
       // intentamos leerlo
       n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
       if n > 0
       then begin
         Inc(BytesRead, n);
         //Write('.');
       end;
     until n <= 0;
     //if BytesRead > 0 then WriteLn;
     M.SetSize(BytesRead);
     //WriteLn('-- ejecutado --');

     S := TStringList.Create;
     S.LoadFromStream(M);
     //WriteLn('-- total de líneas = ', S.Count, ' --');
     {for n := 0 to S.Count - 1 do
     begin
       WriteLn('| ', S[n]);
     end;                  }
     //WriteLn('-- fin --');
     SalidaLista.AddStrings(S);
    // SalidaLista.SaveToFile('C:\test.txt');
     Result := P.ExitStatus;
     S.Free;
     P.Free;
     M.Free;
end;


function ObtenerVelocidadCodificacion(Velocidad: String): String;
Begin
  Case Velocidad Of
    'Placebo' : Result := 'placebo';
    'Muy lento': Result := 'veryslow';
    'Más lento': Result := 'slower';
    'Lento' : Result := 'slow';
    'Medio' : Result := 'medium';
    'Rápido' : Result := 'fast';
    'Más rápido' : Result := 'faster';
    'Muy rápido' : Result := 'veryfast';
    'Super rápido' : Result := 'superfast';
    'Ultra rápido' : Result := 'ultrafast';
  end;
end;

procedure LimpiarAnalisis(var TDatos : TStringList);
var
  i : Integer;
Begin
  // Limpiamos las salidas en blanco
  For i := TDatos.Count - 1 To 0 Do
    If ((TDatos[i] = #13) Or (TDatos[i] = #13#10) Or (TDatos[i] = #10)) Then
       TDatos.Delete(i);
end;

function EjecucionConsola(Comando, Parametros: String; var SalidaLista : TStringList): Integer;
const
  C_BUFSIZE = 2048;
var
  Proceso: TProcess;
  Buffer: pointer;
  SStream: TStringStream;
  nread: longint;
  TLista : TStringList;
  DirectorioTemporal : String;
begin
  DirectorioTemporal := ExtractFilePath(Application.ExeName) + 'temp\';
  // Crear TStringList
  TLista := TStringList.Create;

  Proceso := TProcess.Create(nil);

  // Ejecutar un programa
  Proceso.CommandLine := UTF8ToAnsi(Comando) + ' ' + UTF8ToAnsi(Parametros);
  Proceso.Priority := ppIdle;
  Proceso.CurrentDirectory := DirectorioTemporal;
  Proceso.Options := [poUsePipes, poStderrToOutPut];
  Proceso.Options := Proceso.Options + [poNoConsole];
  Proceso.Execute();


  // Preparando para capturar la salida
  Getmem(Buffer, C_BUFSIZE);
  SStream := TStringStream.Create('');

  // Empezó la captura
  while Proceso.Running do
  begin
    nread := Proceso.Output.Read(Buffer^, C_BUFSIZE);
    if nread = 0 then
      sleep(200)
    else
      begin
        // Convertir la entrada RAW a string
        SStream.size := 0;
        SStream.Write(Buffer^, nread);
        // Y agregar el stringdata al stringlist
        TLista.Add(SStream.DataString);
        // la siguiente linea es solo para pruebas
        //MemoMensajes.Lines.Text := MemoMensajes.Lines.Text + SStream.DataString;
      end;
  end;

  // Capturar lo que queda de la salida
  repeat
    nread := Proceso.Output.Read(Buffer^, C_BUFSIZE);
    if nread > 0 then
    begin
      SStream.size := 0;
      SStream.Write(Buffer^, nread);
      TLista.Add(SStream.Datastring);
      // la siguiente linea es solo para pruebas
      //MemoMensajes.Lines.Text := MemoMensajes.Lines.Text + SStream.DataString;
      Application.ProcessMessages;
//      MemoMensajes.SelStart := Length(MemoMensajes.Lines.Text);
    end
  until nread = 0;

  // Bug Lazarus para esperar a que termine de ejecutarse el archivo ¿?
{  While (Proceso.Active) Do Begin
     Application.ProcessMessages;
     Sleep(25);
     Application.ProcessMessages;
   End;                      }

  // Un poco de limpieza
  Proceso.Free;
  Freemem(buffer);
  SStream.Free;

  LimpiarAnalisis(TLista);
  SalidaLista.AddStrings(TLista);
  SalidaLista.SaveToFile('C:\salida.txt');

  Result := Proceso.ExitStatus;
end;

procedure GenerarAVS(Archivo : String; Subtitulo : String = 'nada');
Var
  TLista : TStringList;
  DirectorioPrograma : String;
Begin
  TLista := TStringList.Create;
  DirectorioPrograma := ExtractFilePath(Application.ExeName);

  TLista.Add('global MeGUI_darx = 16');
  TLista.Add('global MeGUI_dary = 9');
  TLista.Add('LoadPlugin("'+DirectorioPrograma+'tools\ffms\ffms2.dll")');
  TLista.Add('FFVideoSource("'+Archivo+'", threads=1)');
  TLista.Add('#deinterlace');
  TLista.Add('#crop');
  TLista.Add('#resize');
  TLista.Add('#denoise');
  TLista.Add('');
  TLista.Add('LoadPlugin("'+DirectorioPrograma+'tools\VSFilter.dll")');
  If (Subtitulo <> 'nada') then
    TLista.Add('TextSub("'+Subtitulo+'", 1)');

  TLista.SaveToFile(DirectorioPrograma+'temp\perfil.avs');
  TLista.Free;

end;

function BuscarCoincidencias(Lista: TStringList; Const Busqueda: String): TStringList;
Var
  Contador : Integer;
  Temporal : TStringList;
Begin
  Temporal := TStringList.Create;

    for Contador := 0 to Lista.Count-1 do
      if SameText(Lista[Contador], Busqueda) then
         Temporal.Add(Lista[Contador-2]);

    Result := Temporal;
End;

function MkvValido(TDatos : TStringList) : Boolean;
var
  ValidarVideo, ValidarArchivo : Boolean;
Begin
  // Si encuentra esa cadena, estamos bien, existe video
  ValidarVideo := TDatos.IndexOf('|  + Track type: video') <> -1;

  // Si las líneas del resultado del análisis son iguales o
  // menores a 3, significa que el archivo está corrupto
  // o no es un mkv
  ValidarArchivo := TDatos.Count > 3;

  If ((ValidarVideo) And (ValidarArchivo)) Then Result := True
  Else Result := False;
End;

function ObtenerPistasMkv(TDatos : TStringList) : TStringList;
var
  Lista : TStringList;
  Contador : Integer;
Begin
  // En esta lista guardamos las posiciones de los que digan "A Track"
  Lista := TStringList.Create;

  // Recorremos la lista buscando dónde están las pistas
  for Contador := 0 to TDatos.Count - 1 do
    if SameText(TDatos[Contador], '| + A track') then
       Lista.Add(IntToStr(Contador));

  Result := Lista;

end;
end.
